%include "include/sconst.inc"

;由C语言实现
;
extern	gdtcpy
extern	gdt_ptr
extern exception_handler;错误处理函数
extern spurious_irq     ;硬件中断处理函数
extern memset

extern init_idt
extern idt_ptr

extern p_proc_ready
extern tss
extern kernel_main
extern irq_table
extern delay
extern k_reenter

extern sys_call_table

;导出
global _start
global restart

;中断函数相关导出
;错误
global	divide_error
global	single_step_exception
global	nmi
global	breakpoint_exception
global	overflow
global	bounds_check
global	inval_opcode
global	copr_not_available
global	double_fault
global	copr_seg_overrun
global	inval_tss
global	segment_not_present
global	stack_exception
global	general_protection
global	page_fault
global	copr_error
;硬件中断: 两片8259A共15个中断
global  hwint00
global  hwint01
global  hwint02
global  hwint03
global  hwint04
global  hwint05
global  hwint06
global  hwint07
global  hwint08
global  hwint09
global  hwint10
global  hwint11
global  hwint12
global  hwint13
global  hwint14
global  hwint15

global sys_call

[SECTION .bss]
StackSpace		resb	2 * 1024
StackTop:		; 栈顶

[SECTION .text]


_start:
    mov esp, StackTop
    sgdt [gdt_ptr]
    call gdtcpy
    lgdt [gdt_ptr]
    call init_idt
    lidt [idt_ptr]
    jmp	SELECTOR_KERNEL_CS:csinit
csinit:
    xor eax, eax
	mov ax, SELECTOR_TSS
	ltr ax
	jmp kernel_main

    times 16 nop

save:
		pushad
		push ds
		push es
		push fs
		push gs

		mov esi, edx

		mov dx, ss
		mov ds, dx
		mov es, dx
		mov fs, dx
		
		mov edx, esi

		mov esi, esp						;这时的esp指向进程表的首地址

											;通知中断处理完毕
		
		inc dword [k_reenter]
		cmp dword [k_reenter], 0
		jne .1
		mov esp, StackTop 					;切换堆栈
		push restart						;没有中断重入
		jmp [esi + RETADR - P_STACKBASE]	;返回
.1:											;中断重入
		push restart_reenter				;发生中断重入	
		jmp [esi + RETADR - P_STACKBASE]	;返回		


%macro	hwint_master 1

		call save

		in al, INT_M_MASK
		or al, (1 << %1)
		out INT_M_MASK,al

		mov	al,EOI
		out INT_M_CTL, al
		sti ;开中断

		push %1
		call [irq_table + 4 * %1]	;查中断表调用对应的函数
		add esp, 4

		in al, INT_M_MASK
		and al, ~(1 << %1)
		out INT_M_MASK,al


		cli
		
		ret

%endmacro

ALIGN   16
hwint00:                ; Interrupt routine for irq 0 (the clock).
		hwint_master    0



ALIGN   16
hwint01:                ; Interrupt routine for irq 1 (keyboard)
        hwint_master    1

ALIGN   16
hwint02:                ; Interrupt routine for irq 2 (cascade!)
        hwint_master    2

ALIGN   16
hwint03:                ; Interrupt routine for irq 3 (second serial)
        hwint_master    3

ALIGN   16
hwint04:                ; Interrupt routine for irq 4 (first serial)
        hwint_master    4

ALIGN   16
hwint05:                ; Interrupt routine for irq 5 (XT winchester)
        hwint_master    5

ALIGN   16
hwint06:                ; Interrupt routine for irq 6 (floppy)
        hwint_master    6

ALIGN   16
hwint07:                ; Interrupt routine for irq 7 (printer)
        hwint_master    7

; ---------------------------------
%macro  hwint_slave     1
        call save
		in al, INT_S_MASK
		or al, (1 << (%1 - 8))
		out INT_S_MASK, al
		mov al, EOI
		out INT_M_CTL, al
		nop
		out INT_S_CTL, al
		sti
		push %1
		call [irq_table + 4 * %1]
		pop ecx
		cli
		in al, INT_S_MASK
		and al, ~(1 << (%1 - 8))
		out INT_S_MASK, al
		ret
%endmacro
; ---------------------------------

ALIGN   16
hwint08:                ; Interrupt routine for irq 8 (realtime clock).
        hwint_slave     8

ALIGN   16
hwint09:                ; Interrupt routine for irq 9 (irq 2 redirected)
        hwint_slave     9

ALIGN   16
hwint10:                ; Interrupt routine for irq 10
        hwint_slave     10

ALIGN   16
hwint11:                ; Interrupt routine for irq 11
        hwint_slave     11

ALIGN   16
hwint12:                ; Interrupt routine for irq 12
        hwint_slave     12

ALIGN   16
hwint13:                ; Interrupt routine for irq 13 (FPU exception)
        hwint_slave     13

ALIGN   16
hwint14:                ; Interrupt routine for irq 14 (AT winchester)
        hwint_slave     14

ALIGN   16
hwint15:                ; Interrupt routine for irq 15
        hwint_slave     15


divide_error:
	push	0xFFFFFFFF	; no err code
	push	0		; vector_no	= 0
	jmp	exception
single_step_exception:
	push	0xFFFFFFFF	; no err code
	push	1		; vector_no	= 1
	jmp	exception
nmi:
	push	0xFFFFFFFF	; no err code
	push	2		; vector_no	= 2
	jmp	exception
breakpoint_exception:
	push	0xFFFFFFFF	; no err code
	push	3		; vector_no	= 3
	jmp	exception
overflow:
	push	0xFFFFFFFF	; no err code
	push	4		; vector_no	= 4
	jmp	exception
bounds_check:
	push	0xFFFFFFFF	; no err code
	push	5		; vector_no	= 5
	jmp	exception
inval_opcode:
	push	0xFFFFFFFF	; no err code
	push	6		; vector_no	= 6
	jmp	exception
copr_not_available:
	push	0xFFFFFFFF	; no err code
	push	7		; vector_no	= 7
	jmp	exception
double_fault:
	push	8		; vector_no	= 8
	jmp	exception
copr_seg_overrun:
	push	0xFFFFFFFF	; no err code
	push	9		; vector_no	= 9
	jmp	exception
inval_tss:
	push	10		; vector_no	= A
	jmp	exception
segment_not_present:
	push	11		; vector_no	= B
	jmp	exception
stack_exception:
	push	12		; vector_no	= C
	jmp	exception
general_protection:
	push	13		; vector_no	= D
	jmp	exception
page_fault:
	push	14		; vector_no	= E
	jmp	exception
copr_error:
	push	0xFFFFFFFF	; no err code
	push	16		; vector_no	= 10h
	jmp	exception

exception:
	call	exception_handler
	add	esp, 4*2	; 让栈顶指向 EIP，堆栈中从顶向下依次是：EIP、CS、EFLAGS
	hlt

restart:
		mov esp, [p_proc_ready]

		lldt [esp+P_LDT_SEL] ; 立刻加载LDT

		;设置tss的esp0以便中断发生时上边保存寄存器的操作内正确
		;esp0指向该 进程表中寄存器栈的最高位
		lea eax, [esp + P_STACKTOP]
		mov dword [tss+TSS3_S_SP0], eax

restart_reenter:
		dec dword [k_reenter]
		;回复
		pop gs
		pop fs
		pop es
		pop ds
		popad
		add esp, 4
		iretd

sys_call:
	call save
	
	sti
	push esi

	push dword [p_proc_ready]
	push edx
	push ecx
	push ebx
	call [sys_call_table + eax*4]
	add esp, 4*4

	pop esi
	mov	[esi + EAXREG + P_STACKBASE], eax;
	cli

	ret
